﻿using System;
using Notoric.Commanding.Holders;

namespace Notoric.Commanding.CommandBase
{
    /// <summary>
    /// Implements one parameter of a command.
    /// </summary>
    public class Parameter
    {
        #region Constructors

        /// <summary>
        /// Creates a new <see cref="Parameter"/> instance.
        /// </summary>
        /// <param name="name">Name of the parameter</param>
        /// <param name="direction">Direction of the parameter (one of the <see cref="ParameterDirection"/> values)</param>
        /// <param name="valueHolder">Reference to a value holder for the new parameter</param>
        public Parameter(string name, ParameterDirection direction, IValueHolder valueHolder)
        {
            Name = name;
            Direction = direction;

            try
            {
                ValueHolder = valueHolder;
            }
            catch (ArgumentNullException)
            {
                throw new ArgumentNullException("valueHolder");
            }
        }

        #endregion

        #region Public Methods

        /// <summary>
        /// Indicates whether the value of this parameter is known or can be computed at this moment.
        /// </summary>
        /// <remarks>
        /// Returns true if the value of the parameter <see cref="ValueHolder"/> is either in the <see cref="ValueState.Computed"/> state
        /// or the parameter is bound to a command that can be immediately executed.
        /// </remarks>
        /// <param name="parameter">Parameter for the execution</param>
        /// <returns><see langword="true"/> if the value can be computed at this moment, <see langword="false"/> otherwise.</returns>
        public bool CanComputeValue(object parameter)
        {
            return (ValueHolder.ValueState == ValueState.Computed) || (Command != null && Command.CanExecute(parameter));
        }

        #endregion

        #region Public Properties

        /// <summary>
        /// Gets or sets a reference to a command to which this parameter is bound.
        /// </summary>
        /// <value>
        /// <para>
        /// This value is used if the parameter value is bound to the value of another parameter on the referenced command.
        /// </para>
        /// </value>
        public CommandBase Command
        {
            get;
            set;
        }

        /// <summary>
        /// Gets the direction of the parameter.
        /// </summary>
        /// <value>
        /// <para>
        /// One of the <see cref="ParameterDirection"/> values.
        /// </para>
        /// </value>
        public ParameterDirection Direction
        {
            get;
            private set;
        }

        /// <summary>
        /// Gets the name of this parameter.
        /// </summary>
        /// <value>
        /// <para>
        /// The name identifies the parameter in the command parameter set and has to be unique in the scope of the command.
        /// </para>
        /// </value>
        public string Name
        {
            get;
            private set;
        }

        /// <summary>
        /// Gets or sets the value holder of this parameter.
        /// </summary>
        /// <value>
        /// <para>
        /// The value holder used for this parameter. This value is not allowed to be null.
        /// </para>
        /// </value>
        /// <exception cref="ArgumentNullException">
        /// An attempt to set this property to <see langword="null"/> was made.
        /// </exception>
        public IValueHolder ValueHolder
        {
            get { return valueHolder; }
            set
            {
                if (value == null)
                    throw new ArgumentNullException("value");

                valueHolder = value;
            }
        }

        #endregion

        #region Private Fields

        /// <summary>
        /// Holds the value holder of this parameter.
        /// </summary>
        private IValueHolder valueHolder;

        #endregion
    }

    /// <summary>
    /// Defines the direction of a <see cref="Parameter"/>.
    /// </summary>
    public enum ParameterDirection
    {
        /// <summary>
        /// Input parameter
        /// </summary>
        In,
        /// <summary>
        /// Output parameter
        /// </summary>
        Out,
        /// <summary>
        /// InputOutput parameter
        /// </summary>
        InOut
    }
}
